/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 spell_displacement.h

	$Header: /heroes4/spell_displacement.h $

	$NoKeywords: $

 ************************************************************************/
// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "spell_displacement.h"

#include "adaptor_handler.h"
#include "angle.h"
#include "animated_window.h"
#include "animation.h"
#include "battlefield.h"
#include "bitmap_cursor.h"
#include "bitmap_group_cache.h"
#include "bound_handler.h"
#include "can_cast.h"
#include "combat_creature.h"
#include "combat_spell_registration.h"
#include "direction.h"
#include "mouse_window.h"
#include "spell_actor_animation.h"
#include "translation_animation.h"

static t_combat_spell_registration<t_spell_displacement>
       k_sparks_register( k_spell_displacement );

static t_bitmap_group_cache const k_bitmaps[16] =
{
	"cursor.combat.arrow.northeast",
	"cursor.combat.arrow.east_northeast",
	"cursor.combat.arrow.east",
	"cursor.combat.arrow.east_southeast",
	"cursor.combat.arrow.southeast",
	"cursor.combat.arrow.south_southeast",
	"cursor.combat.arrow.south",
	"cursor.combat.arrow.south_southwest",
	"cursor.combat.arrow.southwest",
	"cursor.combat.arrow.west_southwest",
	"cursor.combat.arrow.west",
	"cursor.combat.arrow.west_northwest",
	"cursor.combat.arrow.northwest",
	"cursor.combat.arrow.north_northwest",
	"cursor.combat.arrow.north",
	"cursor.combat.arrow.north_northeast",
};

// -----------------------------------------------------------------
// displacement spell
// -----------------------------------------------------------------
t_spell_displacement::t_spell_displacement( t_battlefield& battlefield, t_spell spell )
                    : t_combat_spell_single_target( battlefield, spell )
{
	int i;

	m_last_angle = -1;
	for (i = 0; i < k_cursor_count; i++)
		m_bitmaps[i] = k_bitmaps[i].get();
}
	
// -----------------------------------------------------------------
// displacement spell
// -----------------------------------------------------------------
t_mouse_window* t_spell_displacement::get_cursor( int angle )
{
	if (angle == m_last_angle)
		return m_last_cursor;

	t_bitmap_cursor cursor;

	cursor.create( m_bitmaps[angle] );
	m_last_cursor = cursor.cursor;

	// add spell animation
	t_animation_ptr spell_cursor = m_battlefield.get_spell_cursor_animation();
	t_screen_point  end_arrow;
	t_screen_point  origin;
	t_window*       window;

	end_arrow = m_bitmaps[angle]->find( "end_point" )->get_rect().top_left();
	origin = end_arrow - spell_cursor->get_rect().size() / 2;
	window = new t_animated_window( spell_cursor, origin, 100, cursor.cursor );

	// recompute hot spot
	t_screen_point hot_spot;

	origin = cursor.frames->get_screen_rect().top_left();
	origin = cursor.cursor->to_client( origin );
	cursor.cursor->set_hot_spot( cursor.cursor->get_hot_spot() + origin );
	return cursor.cursor;
}

// -----------------------------------------------------------------
// displacement spell
// -----------------------------------------------------------------
t_map_point_3d find_displacement_destination( t_combat_creature const& target,
											  t_map_point_2d delta )
{
	int const            k_max_distance = 8;
	int const            k_max_distance_squared = k_max_distance * k_max_distance;
	t_battlefield const& battlefield = target.get_battlefield();
	t_map_point_2d       position = target.get_cell_position();
	t_map_point_2d		 result;
	t_map_point_2d		 new_point;
	t_map_point_2d       difference;
	t_map_point_2d       sum(0,0);
	int				     divisor;
	int					 i;

	divisor = abs( delta.column );
	if ( divisor < abs( delta.row ))
		divisor = abs( delta.row );
	divisor <<= k_battlefield_subcell_shift;
	delta <<= k_battlefield_subcell_shift;
	result = position;
	for (i = 0; i < k_max_distance; i++)
	{
		sum += delta;
		difference = sum / divisor;
		if (difference.column * difference.column + difference.row * difference.row 
			> k_max_distance_squared)
			break;
		new_point = position + difference;
		if (!battlefield.can_place( target, new_point ))
			break;
		result = new_point;
	}

	t_map_point_3d return_value( result << k_battlefield_subcell_shift, 0 );

	return_value.height = battlefield.compute_terrain_height_under_object( target,
            		                                                      return_value );
	return return_value;
}
 
// -----------------------------------------------------------------
// displacement spell
// -----------------------------------------------------------------
void t_spell_displacement::displace( t_combat_creature_ptr target, t_combat_action_message message )
{
	t_map_point_3d position = find_displacement_destination( *target, m_delta );
	
	t_translation_animation_ptr animation = new t_translation_animation( *target, position );
	animation->display_action_message( message );

	target->set_animation( animation );
}

static t_combat_actor_model_cache_set k_cache( "spells.displacement" );

// -----------------------------------------------------------------
// displacement spell
// -----------------------------------------------------------------
void t_spell_displacement::cast_and_mirror( t_combat_creature_ptr target, int power )
{
	// create sparks animations
	t_direction	   			direction;
	t_combat_actor*			image;
	double					scale = m_battlefield.get_model_scale();
	t_map_point_2d			position;
	t_handler				handler;
	int						angle = get_angle( m_delta );
	t_combat_action_message message( *m_caster, get_action_text( false ) );
	t_spell_actor_animation_ptr animation;

	play_sound();
	direction = get_direction( angle );
	image = new t_unsaved_combat_actor( &m_battlefield, k_cache.get( scale ),
		                        k_combat_actor_action_cast_spell, direction );
	image->move( m_battlefield, t_map_point_3d(0,0,0) );
	position = target->get_footprint_center();
	position -= image->get_footprint_center();
	position += get_direction_offset( t_direction((direction + 4) & 7) )
		        * (target->get_footprint_size() << (k_battlefield_subcell_shift - 1));
	m_battlefield.place_object( image, position );
	handler = add_argument( add_1st_argument( bound_handler( *this, &t_spell_displacement::displace ),
										      target ), message );

	animation = new t_spell_actor_animation( m_battlefield, *image, this, handler );
	animation->display_action_message( message );

	image->set_animation( animation.get() );
}

double t_spell_displacement::ai_weight( t_combat_creature const& target ) const
{
	return 0.0;
}

double t_spell_displacement::get_cancel_weight( t_combat_creature const& target ) const
{
	return 0.0;
}

// -----------------------------------------------------------------
// Displacement spell
// -----------------------------------------------------------------
t_combat_ai_action_list t_spell_displacement::generate_combat_ai_action_list(t_combat_ai &owner)
{
	// Not implemented
	return t_combat_ai_action_list();
}

// -----------------------------------------------------------------
// single target curse-type spells
// -----------------------------------------------------------------

t_mouse_window* t_spell_displacement::mouse_move( t_screen_point const& point,
															 std::string& help_text )
{
	t_combat_creature* creature = m_battlefield.creature_hit_test( point );
	t_mouse_window*    result;

	m_battlefield.select_target( creature );
	result = m_battlefield.get_blocked_cursor();
	if (creature == 0)
		return result;

	result = m_battlefield.get_spell_cannot_cast_cursor();
	if (::can_cast( *m_caster, m_spell, *creature, &help_text ))
	{
		t_map_point_3d target_center = creature->get_footprint_center();
		t_screen_point target_screen_point = m_battlefield.subcell_to_screen_point( target_center );
		int            angle = get_map_angle( point, target_screen_point );
		int            cursor_angle = (angle * k_cursor_count + 180) / 360;

		result = get_cursor( cursor_angle % k_cursor_count );
	}
	return result;
}

// -----------------------------------------------------------------
// single target curse-type spells
// -----------------------------------------------------------------
bool t_spell_displacement::left_click( t_screen_point const& point )
{
	t_combat_creature* creature = m_battlefield.creature_hit_test( point );

	if (creature == 0)
		return false;

	if (!::can_cast( *m_caster, m_spell, *creature ))
		return false;

	t_map_point_3d target_center = creature->get_footprint_center();
	t_screen_point target_screen_point = m_battlefield.subcell_to_screen_point( target_center );
	int            angle = get_map_angle( point, target_screen_point );

	m_delta.row = -cos( angle * k_pi / 180.0 ) * 10;
	m_delta.column = sin( angle * k_pi / 180.0 ) * 10;
	m_spell_target = creature;
	if (!m_is_artifact)
		get_caster()->set_default_spell( m_spell );	
	m_battlefield.begin_spell( creature );
	return true;
}
